#include "Avalon/Serialization/BinarySerializer.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::Serialization;
using namespace std;

BinarySerializer::BinarySerializer()
    : m_writeIterator(m_buffer.GetMutableData()) {}

BinarySerializer::~BinarySerializer() {}

Buffer BinarySerializer::GetSerializedData() {
  Buffer serializedData;
  serializedData.Append(m_buffer.GetMutableData(), m_buffer.GetSize());
  m_buffer.Reset();
  m_writeIterator = m_buffer.GetMutableData();
  return serializedData;
}

void BinarySerializer::Shuttle(const char* name, bool& value) {
  SerializePrimitive(value);
}

void BinarySerializer::Shuttle(const char* name, char& value) {
  SerializePrimitive(value);
}

void BinarySerializer::Shuttle(const char* name, short& value) {
  SerializePrimitive(value);
}

void BinarySerializer::Shuttle(const char* name, int& value) {
  SerializePrimitive(value);
}

void BinarySerializer::Shuttle(const char* name, long& value) {
  SerializePrimitive(value);
}

void BinarySerializer::Shuttle(const char* name, long long& value) {
  SerializePrimitive(value);
}

void BinarySerializer::Shuttle(const char* name, float& value) {
  SerializePrimitive(value);
}

void BinarySerializer::Shuttle(const char* name, double& value) {
  SerializePrimitive(value);
}

void BinarySerializer::Shuttle(const char* name, string& value) {
  int size = static_cast<int>(value.size());
  SerializePrimitive(size);
  ResizeIfNeeded(size);
  memcpy(m_writeIterator, value.c_str(), size);
  m_writeIterator += size;
}

void BinarySerializer::Shuttle(const char* name, Buffer& data) {
  SerializePrimitive(data.GetSize());
  ResizeIfNeeded(data.GetSize());
  memcpy(m_writeIterator, data.GetData(), data.GetSize());
  m_writeIterator += data.GetSize();
}

template<typename T>
void BinarySerializer::SerializePrimitive(T value) {
  ResizeIfNeeded(sizeof(T));
  *reinterpret_cast<T*>(m_writeIterator) = value;
  m_writeIterator += sizeof(T);
}

void BinarySerializer::ResizeIfNeeded(int additionalSizeRequired) {
  if(static_cast<int>((m_writeIterator + additionalSizeRequired) -
      m_buffer.GetData()) <= m_buffer.GetSize()) {
    return;
  }
  char* oldBuffer = m_buffer.GetMutableData();
  m_buffer.Grow(additionalSizeRequired);
  m_writeIterator = m_buffer.GetMutableData() + (m_writeIterator - oldBuffer);
}
